# Finshake
# Contains logic to start the finshake (=handshake) and to
# search for possible friends.
class Finshake

  def self.build_ip_range(start_ip, range)
    ip_range = []
    for i in 1..range do
      for ii in 1..2
        if ii == 1
          ip_range << Integer(start_ip) + i
        else
          ip_range << Integer(start_ip) - i
        end
      end
    end
    ip_range
  end

  # Find a friend
  # +referrer+:: the reffer
  def self.find_friend(fishtank, referrer)
    found_friend = false
    friends_uri = nil
    myself_uri = fishtank.get_myself()['uri']

    split_ip = $LOCAL_IP.split(/\./)
    subnet = "#{split_ip[0]}.#{split_ip[1]}.#{split_ip[2]}."
    $L.info "Searching for a friend in sub net #{subnet}"

    # sarching for hosts which are up
    for i in build_ip_range(split_ip[3], 2)
      ip_to_check = "#{subnet}#{i}"
      $L.debug "  checking if host #{ip_to_check} is up"


      resource = RestClient::Resource.new "#{alive_uri}", :timeout => 0.1, :open_timeout => 0.1
      resource.get()

      begin
        timeout(1) do
          s = TCPSocket.open(ip_to_check, 4567)
        end
      rescue
        puts "timeout"
      end
      out = s.gets
      p out
      #res = UDPSocket.open do |s|
      #  s.connect ip_to_check, 4567
      #end
      #p res
      #res = %x( ping -n 1 #{ip_to_check})
      #p $?.exitstatus
      #p res
      #result = `ping -q -c #{ping_count} #{check_ip}`
      #tcp_host = Net::Ping::TCP.new(ip_to_check, p, 0.2)
      #if tcp_host.ping?
      #  $L.debug "    is up"
      #else
      #  $L.debug "    is down"
      #end
    end

    #begin
    #  for i in 0..5 do
    #    for ii in 1..2
    #      if ii == 1
    #        check_ip = "#{split_ip[0]}.#{split_ip[1]}.#{split_ip[2]}.#{Integer(split_ip[3]) + i}"
    #      else
    #        check_ip = "#{split_ip[0]}.#{split_ip[1]}.#{split_ip[2]}.#{Integer(split_ip[3]) - i}"
    #      end
    #
    #      $FFF_PORT_RANGE.each do |p|
    #
    #        ping_count = 1
    #        tcp = Net::Ping::TCP.new(check_ip, p, 0.2)
    #        #result = `ping -q -c #{ping_count} #{check_ip}`
    #        #if $?.exitstatus == 0
    #        if !tcp.ping?
    #          $L.debug "    skipping #{check_ip}:#{p}, host not reachable"
    #        else
    #          check_uri = "#{$PROTOCOL}://#{check_ip}:#{p}"
    #          alive_uri = "#{check_uri}/alive"
    #          $L.debug "  at #{check_uri}, myself #{myself_uri}, referrer #{referrer}"
    #          # omit checks
    #          # 1. on ourselves (should never happen, but let's check for it either way)
    #          # 2. on the referrer
    #          if check_uri == myself_uri
    #            $L.debug "    skipping #{check_uri} (is myself)"
    #          else
    #            if check_uri == referrer
    #              $L.debug "    skipping #{check_uri} (is referrer)"
    #            else
    #              if fishtank.get_friends.first(:uri => check_uri)
    #                $L.debug "    skipping #{check_uri} (is already in fish db)"
    #              else
    #                $L.debug "    opening resource #{alive_uri} with timeout => #{$FFF_TIMEOUT}"
    #
    #                begin
    #                  timeout($FFF_TIMEOUT) do
    #                    open(alive_uri, 'r', :read_timeout=>$FFF_TIMEOUT) do |http|
    #                      if http.read.code == "200"
    #                        $L.debug "  found one at #{friends_uri}"
    #                        friends_uri = check_uri
    #                        found_friend = true
    #                      end
    #                    end
    #
    #                    #def UrlAvailable?(urlStr)
    #                    #  url = URI.parse(urlStr)
    #                    #  Net::HTTP.start(urlStr.host, urlStr.port) do |http|
    #                    #    return http.head(urlStr.request_uri).code == "200"
    #                    #  end
    #                    #end
    #                  end
    #                rescue
    #                  # do nothing
    #                end
    #              end
    #            end
    #          end
    #        end
    #      end
    #    end
    #  end
    #end while not found_friend

    friends_uri
  end

# Initiate the FinShake by a GET
# +referrer+:: the referrer
  def self.init_finshake(fishtank, referrer)
    $L.info "Finshake started by REFERRER #{referrer}"
    friend = find_friend(fishtank, referrer)
    if friend.nil?
      $L.info "  not able to find a friend or all friends already known"
    else
      $L.debug "  found one at URI #{friend}, sending request and parsing responds"
      $L.debug "  Request:  " + fishtank.get_myself().to_json
      responds = RestClient.put("#{friend}/finshake", fishtank.get_myself().to_json, :referrer => fishtank.get_myself()['uri'])
      $L.debug "  Responds: " + responds
      data = JSON.parse(responds)
      if data.nil? or !data.has_key?('uuid') or !data.has_key?('uri')
        $L.warn "  responds was invalid!"
      else
        fishtank.create_fish_friend(data['uuid'], data['uri'], data['os'])
        $L.info "  now friend with #{friend}"

        # last but not least, trigger a friend seach on friends side if in swarm mode
        if $SWARM_MODE
          $L.info "Triggering finshake on #{friend}/finshake using REFERRER #{fishtank.get_myself()['uri']}"
          RestClient.get("#{friend}/finshake", :referrer => fishtank.get_myself()['uri'])
        end

      end
    end
  end

end